package whf.framework.workflow.client;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.taskmgmt.exe.TaskInstance;

import whf.framework.util.ThreadContext;
import whf.framework.util.Utils;
import whf.framework.workflow.engine.ExtendedTaskInstance;
import whf.framework.workflow.engine.WorkflowConfig;
import whf.framework.workflow.engine.WorkflowConstants;
import whf.framework.workflow.engine.WorkflowEngine;
import whf.framework.workflow.entity.WorkflowEntity;
import whf.framework.workflow.exception.WorkflowException;
import whf.framework.workflow.ext.ProcessNodeDescriptor;

/**
 * @author king
 * @modify May 15, 2008 10:43:45 PM
 * 
 */
@SuppressWarnings("unchecked")
public class WorkflowClient{
	private static ThreadLocal<WorkflowClient> threadContext = new ThreadLocal<WorkflowClient>();

	public final static String PROCESSABLE_IDENTITY = "processInstanceId";

	private WorkflowEngine workflowEngine = WorkflowConfig.getWorkflowEngine();

	private ProcessInstance processInstance;

	private WorkflowEntity workflowEntity;

	private String username;

	private Collection<TaskInstance> tasks = null;

	private List<ProcessNodeDescriptor> processFieldDescriptors = null;

	public static WorkflowClient getWorkflowClient(WorkflowEntity entity) {
		return getWorkflowClient(entity, ThreadContext.getUserInUserContext().getUsername());
	}
	
	public static WorkflowClient getWorkflowClient(WorkflowEntity entity, String username) {
		WorkflowClient client = threadContext.get();
		if (client != null) {
			if (client.workflowEntity != entity || StringUtils.equalsIgnoreCase(username, client.username)) {
				client = null;
			}
		}
		if (client == null) {
			if (entity == null && username == null) {
				return null;
			}
			client = new WorkflowClient(entity, username);
			threadContext.set(client);
		}
		return client;
	}

	private WorkflowClient(WorkflowEntity entity, String username) {
		assert entity != null : "formValue不能空！";
		this.workflowEntity = entity;
		this.username = username;
	}

	/**
	 * 判定一个实体是否流程性质
	 * 
	 * @return
	 * @throws WorkflowException
	 */
	public boolean isProcessableObject() {
		return true;
	}

	/**
	 * 使用当前业务实体的entityName启动流程实例
	 * 
	 * @return
	 * @throws WorkflowException
	 * @throws GenericEntityException
	 */
	public ProcessInstance startProcessInstance() throws WorkflowException {
		ProcessDefinition pd = null;
		return this.startProcessInstance(pd);
	}

	/**
	 * 启动流程实例
	 * 
	 * @param entityName
	 *            流程对应的实例,扩展一个实体对应多个流程的情况
	 * @return 流程实例
	 * @throws WorkflowException
	 * @throws GenericEntityException
	 */
	public ProcessInstance startProcessInstance(ProcessDefinition processDefinition) throws WorkflowException {
		if (!this.isProcessableObject()) {
			throw new WorkflowException("对象不是流程对象！");
		}
		ProcessInstance pi = this.workflowEngine.getProcessInstance(this.workflowEntity.getProcessInstanceId());
		if (pi != null) {
			throw new WorkflowException("Process Instance on this entity exists already!");
		}
		// 启动流程；
		String key = String.valueOf(System.currentTimeMillis());
		String[] keys = {"id", "name", "code", "remarks"};
		for(String k: keys) {
			key += "|" + this.workflowEntity.getProperty(k);
		}
		Map<String, Object> attrs = Utils.newHashMap();
		attrs.put(WorkflowConstants.CONTEXT_VARIABLE_ENTITY_ID, String.valueOf(this.workflowEntity.getId()));
		attrs.put(WorkflowConstants.CONTEXT_VARIABLE_ENTITY_TYPE, this.workflowEntity.getClass().getName());
		attrs.put("KEY", key);
		// start the process and load the task instance for current user
		this.processInstance = workflowEngine.startProcessInstance(processDefinition, this.workflowEntity, attrs);
		//
		return processInstance;
	}

	/**
	 * 终止流程实例，用在对象删除时
	 * 
	 * @throws WorkflowException
	 * @throws GenericEntityException
	 */
	public void terminateProcessInstance() throws WorkflowException {
		workflowEngine.terminateProcessInstance(this.getProcessInstance());
	}

	/**
	 * 获取当前业务实体的流程定义
	 * 
	 * @return
	 * @throws WorkflowException
	 */
	public ProcessDefinition getProcessDefinition() throws WorkflowException {
		ProcessInstance pi = this.getProcessInstance();
		if (pi != null) {
			return pi.getProcessDefinition();
		} else {
			return null;
		}
	}

	/**
	 * 根据实体名称获取发布的对应的流程定义
	 * 
	 * @param entityName
	 * @return
	 * @throws WorkflowException
	 */
	public static ProcessDefinition getProcessDefinition(String entityName) throws WorkflowException {
		return null;
	}

	public ProcessInstance getProcessInstance() throws WorkflowException {
		if (this.processInstance == null && this.workflowEntity.getProcessInstanceId() > 0) {
			this.processInstance = this.workflowEngine.getProcessInstance(this.workflowEntity.getProcessInstanceId());
		}
		return this.processInstance;
	}

	public Collection<TaskInstance> getMyTasks() throws WorkflowException {
		if (tasks != null) {
			return tasks;
		} else {
			tasks = workflowEngine.findTaskInstancesByActor(this.username);
			tasks.addAll(this.getAgentTasks());
		}
		return tasks;
	}

	public Collection<TaskInstance> getAgentTasks() throws WorkflowException {
		return null;
	}

	public boolean hasTasks() throws WorkflowException {
		if (!this.isProcessableObject())
			return false;
		return this.getMyTasks().size() > 0;
	}

	public Token getRootToken() throws WorkflowException {
		ProcessInstance pi = this.getProcessInstance();
		Token token = pi.getRootToken();
		return token;
	}

	public WorkflowEntity getFormValue() {
		return this.workflowEntity;
	}
	
	public List<ProcessNodeDescriptor> getProcessFieldDescriptors() {
		return processFieldDescriptors;
	}

	public List<String> getValidationMessages() throws WorkflowException {
		return null;
	}
	
	/**
	 * @param action
	 *            动作名称
	 * @param transitionName
	 *            转换名称
	 * @throws WorkflowException
	 * @throws GenericEntityException
	 */
	public void processTask(String action, String transitionName, Map<String, String> params) throws WorkflowException {
		Collection<TaskInstance> tasks = this.getMyTasks();

		// 处理流程Task
		if (tasks != null && tasks.size() > 0) {
			for (TaskInstance task : tasks) {
				try {
					if (task instanceof ExtendedTaskInstance) {
						ExtendedTaskInstance ti = (ExtendedTaskInstance) task;
						if (StringUtils.isEmpty(transitionName)) {
							if (task.getTask() != null && task.getTask().getTaskNode() != null
									&& task.getTask().getTaskNode().getLeavingTransitions() != null
									&& task.getTask().getTaskNode().getLeavingTransitions().size() == 1) {
								transitionName = ((Transition) task.getTask().getTaskNode().getLeavingTransitions().get(0)).getName();
							}
						}
						task.getTask().getTaskNode().getLeavingTransitions();
						ti.setTransition(transitionName);
						if (params.get("comment") != null)
							ti.setComment(params.get("comment"));

						if (params.get("description") != null)
							ti.setDescription(params.get("description"));

						if (StringUtils.isEmpty(ti.getActorId())) {
							ti.setActorId(this.username);
						}
					}

					Map variables = new HashMap();
					variables.put("entity", this.workflowEntity);
					variables.put("userValue", this.username);
					task.addVariables(variables);

					if ("start".equals(action)) {
						task.start();
					} else if ("suspend".equals(action)) {
						task.suspend();
					} else if ("resume".equals(action)) {
						task.resume();
					} else {
						if (StringUtils.isEmpty(transitionName)) {
							task.end();
						} else {
							task.end(transitionName);
						}
					}
				} catch (Exception e) {
					throw new WorkflowException(e);
				}
			}
		}

	}

	public String getWebFlowXml() throws WorkflowException {
		return WebflowXmlBuilder.buildXml(this.getProcessInstance());
	}
}
